using System;
using System.Collections.Generic;
using System.Text;

namespace Rapier.Kodachi.Firefox
{
    public abstract class Node : IComparable<Node>, IComparable
    {
        protected Node _parent = null;
        private List<Node> _children = new List<Node>();

        private static Comparison<Node> _comparison;

        protected static Comparison<Node> Comparison
        {
            get { return _comparison; }
            set { _comparison = value; }
        }


        public Node()
        {

        }


        public Node Parent
        {
            get { return _parent; }
        }

        private List<Node> Children
        {
            get { return _children; }
        }

        public IEnumerable<Node> GetChildren()
        {
            foreach (Node node in Children)
            {
                yield return node;
            }
        }

        public void Remove(Node node)
        {
            if (!Children.Contains(node))
            {
                const string message = "Node node was not found in child collection. Node {0}.";
                throw new ArgumentOutOfRangeException(string.Format(message, ToString()));
            }
            if (node.HasChildren)
            {
                throw new ArgumentException("Cannot remove node. Children collection is not empty");
            }
            Children.Remove(node);
        }

        public void Add(Node node)
        {
            if (Children.Contains(node))
            {
                throw new ArgumentOutOfRangeException("Node node was found in child collection");
            }
            Children.Add(node);
        }

        public static void SetRelationShip(Node parent, Node child)
        {
            parent.Add(child);
            child._parent = parent;
        }

        public static void DeleteRelationShip(Node parent, Node child)
        {
            parent.Remove(child);
            child._parent = null;
        }

        #region IComparable<Node> Members

        public int CompareTo(Node other)
        {
            return Comparison(this, other);
        }

        #endregion

        public bool HasChildren { get { return this.Children.Count > 0; } }

        public T Find<T>(Predicate<T> predicate) where T : Node
        {
            return Find((T)this, predicate);
        }

        private T Find<T>(T parent, Predicate<T> predicate) where T : Node
        {
            foreach (T node in FindAll(parent, predicate))
            {
                return node;
            }
            return null;
        }

        public static IEnumerable<T> GetAll<T>(T fromNode) where T : Node
        {
            foreach (Node child in fromNode.Children)
            {
                foreach (Node node in GetAll(child))
                {
                    yield return (T)node;
                }
            }
            yield return fromNode;
        }

        private static IEnumerable<T> FindAll<T>(T parent, Predicate<T> predicate) where T : Node
        {
            foreach (T child in parent.GetChildren())
            {
                foreach (T node in FindAll(child, predicate))
                {
                    yield return node;
                }
            }
            if (predicate(parent))
            {
                yield return parent;
            }
        }

        public IEnumerable<T> FindAll<T>(Predicate<T> predicate) where T : Node
        {
            return FindAll((T)this, predicate);
        }


        #region IComparable Members

        public int CompareTo(object obj)
        {
            return this.CompareTo((Node)obj);
        }

        public abstract bool IsValid { get; }

        #endregion
    }
}
